/* ===========================================================
* TradeManager : a application to trade strategies for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Project Info: org.trade
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Oracle, Inc.
* in the United States and other countries.]
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Original Author: Simon Allen;
* Contributor(s): -;
*
* Changes
* -------
*
*/
package org.trade.ui.base;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterAbortException;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.text.MessageFormat;
import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.swing.JComponent;
import javax.swing.JTable.PrintMode;
import javax.swing.SwingUtilities;
/**
*
* @version $Id: JComponentVista.java,v 1.2 2001/10/22 18:55:37 simon Exp $
* @author Simon Allen
*/
public class ComponentPrintService extends PrintPage implements Printable {
private double m_ScaleX;
private double m_ScaleY;
/**
* The Swing component to print.
*/
private Component m_component = null;
private Throwable printError;
/**
* Create a Pageable that can print a Swing JComponent over multiple pages.
*
* @param c
* The swing JComponent to be printed.
*
* @param format
* The size of the pages over which the componenent will be
* printed.
*/
public ComponentPrintService(Component c, PageFormat format) {
setPageFormat(format);
setPrintable(this);
setComponent(c);
/*
* Tell the Vista we subclassed the size of the canvas.
*/
Rectangle componentBounds = c.getBounds(null);
setSize(componentBounds.width, componentBounds.height);
setScale(1, 1);
}
/**
* Method setComponent.
*
* @param c
* Component
*/
protected void setComponent(Component c) {
m_component = c;
}
/**
* Method setScale.
*
* @param scaleX
* double
* @param scaleY
* double
*/
protected void setScale(double scaleX, double scaleY) {
m_ScaleX = scaleX;
m_ScaleY = scaleY;
}
public void scaleToFitX() {
PageFormat format = getPageFormat();
Rectangle componentBounds = m_component.getBounds(null);
double scaleX = format.getImageableWidth() / componentBounds.width;
double scaleY = scaleX;
if (scaleX < 1) {
setSize((float) format.getImageableWidth(), (float) (componentBounds.height * scaleY));
setScale(scaleX, scaleY);
}
}
public void scaleToFitY() {
PageFormat format = getPageFormat();
Rectangle componentBounds = m_component.getBounds(null);
double scaleY = format.getImageableHeight() / componentBounds.height;
double scaleX = scaleY;
if (scaleY < 1) {
setSize((float) (componentBounds.width * scaleX), (float) format.getImageableHeight());
setScale(scaleX, scaleY);
}
}
/**
* Method scaleToFit.
*
* @param useSymmetricScaling
* boolean
*/
public void scaleToFit(boolean useSymmetricScaling) {
PageFormat format = getPageFormat();
Rectangle componentBounds = m_component.getBounds(null);
double scaleX = format.getImageableWidth() / componentBounds.width;
double scaleY = format.getImageableHeight() / componentBounds.height;
if ((scaleX < 1) || (scaleY < 1)) {
if (useSymmetricScaling) {
if (scaleX < scaleY) {
scaleY = scaleX;
} else {
scaleX = scaleY;
}
}
setSize((float) (componentBounds.width * scaleX), (float) (componentBounds.height * scaleY));
setScale(scaleX, scaleY);
}
}
/**
* Method print.
*
* @param graphics
* Graphics
* @param pageFormat
* PageFormat
* @param pageIndex
* int
* @return int
* @throws PrinterException
* @see java.awt.print.Printable#print(Graphics, PageFormat, int)
*/
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) graphics;
g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
Rectangle componentBounds = m_component.getBounds(null);
g2.translate(-componentBounds.x, -componentBounds.y);
g2.scale(m_ScaleX, m_ScaleY);
boolean wasBuffered = disableDoubleBuffering(m_component);
m_component.printAll(g2);
restoreDoubleBuffering(m_component, wasBuffered);
return PAGE_EXISTS;
}
/**
* Method print.
*
* @return boolean
* @throws Exception
*/
public boolean print() throws Exception {
boolean showDialogs = !GraphicsEnvironment.isHeadless();
return print(PrintMode.FIT_WIDTH, null, null, showDialogs, null, showDialogs, null);
}
/**
* Method print.
*
* @param printMode
* PrintMode
* @param headerFormat
* MessageFormat
* @param footerFormat
* MessageFormat
* @param showPrintDialog
* boolean
* @param attr
* PrintRequestAttributeSet
* @param interactive
* boolean
* @param service
* PrintService
* @return boolean
* @throws Exception
*/
public boolean print(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat,
boolean showPrintDialog, PrintRequestAttributeSet attr, boolean interactive, PrintService service)
throws Exception {
// complain early if an invalid parameter is specified for headless mode
boolean isHeadless = GraphicsEnvironment.isHeadless();
if (isHeadless) {
if (showPrintDialog) {
throw new HeadlessException("Can't show print dialog.");
}
if (interactive) {
throw new HeadlessException("Can't run interactively.");
}
}
// Get a PrinterJob.
// Do this before anything with side-effects since it may throw a
// security exception - in which case we don't want to do anything else.
final PrinterJob job = PrinterJob.getPrinterJob();
if (attr == null) {
attr = new HashPrintRequestAttributeSet();
}
// fetch the Printable
Printable printable = this;
DocFlavor doc_flavor = DocFlavor.INPUT_STREAM.JPEG;
PrintRequestAttributeSet attr_set = new HashPrintRequestAttributeSet();
PrintService[] services = PrintServiceLookup.lookupPrintServices(doc_flavor, attr_set);
if (services.length > 0) {
service = services[1];
}
if (interactive) {
// wrap the Printable so that we can print on another thread
printable = new ThreadSafePrintable(printable);
}
// set the printable on the PrinterJob
job.setPrintable(printable);
// if specified, set the PrintService on the PrinterJob
if (service != null) {
job.setPrintService(service);
}
// if requested, show the print dialog
if (showPrintDialog && !job.printDialog(attr)) {
// the user cancelled the print dialog
return false;
}
// if not interactive, just print on this thread (no dialog)
if (!interactive) {
// do the printing
job.print(attr);
// we're done
return true;
}
// make sure this is clear since we'll check it after
printError = null;
// to synchronize on
final Object lock = new Object();
// copied so we can access from the inner class
final PrintRequestAttributeSet copyAttr = attr;
// this runnable will be used to do the printing
// (and save any throwables) on another thread
Runnable runnable = new Runnable() {
public void run() {
try {
// do the printing
job.print(copyAttr);
} catch (Throwable t) {
// save any Throwable to be rethrown
synchronized (lock) {
printError = t;
}
}
}
};
// start printing on another thread
Thread th = new Thread(runnable, "PrinterThread");
th.start();
// look for any error that the printing may have generated
Throwable pe;
synchronized (lock) {
pe = printError;
printError = null;
}
// check the type of error and handle it
if (pe != null) {
// a subclass of PrinterException meaning the job was aborted,
// in this case, by the user
if (pe instanceof PrinterAbortException) {
return false;
} else if (pe instanceof PrinterException) {
throw (PrinterException) pe;
} else if (pe instanceof Exception) {
throw (Exception) pe;
} else if (pe instanceof Error) {
throw (Error) pe;
}
// can not happen
throw new AssertionError(pe);
}
return true;
}
/**
* Method disableDoubleBuffering.
*
* @param c
* Component
* @return boolean
*/
private boolean disableDoubleBuffering(Component c) {
if (!(c instanceof JComponent))
return false;
JComponent jc = (JComponent) c;
boolean wasBuffered = jc.isDoubleBuffered();
jc.setDoubleBuffered(false);
return wasBuffered;
}
/**
* Method restoreDoubleBuffering.
*
* @param c
* Component
* @param wasBuffered
* boolean
*/
private void restoreDoubleBuffering(Component c, boolean wasBuffered) {
if (c instanceof JComponent)
((JComponent) c).setDoubleBuffered(wasBuffered);
}
/**
*/
private class ThreadSafePrintable implements Printable {
/** The delegate <code>Printable</code>. */
private Printable printDelegate;
/**
* To communicate any return value when delegating.
*/
private int retVal;
/**
* To communicate any <code>Throwable</code> when delegating.
*/
private Throwable retThrowable;
/**
* Construct a <code>ThreadSafePrintable</code> around the given
* delegate.
*
* @param printDelegate
* the <code>Printable</code> to delegate to
*/
public ThreadSafePrintable(Printable printDelegate) {
this.printDelegate = printDelegate;
}
/**
* Prints the specified page into the given {@link Graphics} context, in
* the specified format.
* <p>
* Regardless of what thread this method is called on, all calls into
* the delegate will be done on the event-dispatch thread.
*
* @param graphics
* the context into which the page is drawn
* @param pageFormat
* the size and orientation of the page being drawn
* @param pageIndex
* the zero based index of the page to be drawn
*
*
* @return PAGE_EXISTS if the page is rendered successfully, or
* NO_SUCH_PAGE if a non-existent page index is specified
* * @throws PrinterException if an error causes printing to be
* aborted * @see java.awt.print.Printable#print(Graphics,
* PageFormat, int)
*/
public int print(final Graphics graphics, final PageFormat pageFormat, final int pageIndex)
throws PrinterException {
// We'll use this Runnable
Runnable runnable = new Runnable() {
public synchronized void run() {
try {
// call into the delegate and save the return value
retVal = printDelegate.print(graphics, pageFormat, pageIndex);
} catch (Throwable throwable) {
// save any Throwable to be rethrown
retThrowable = throwable;
} finally {
// notify the caller that we're done
notifyAll();
}
}
};
synchronized (runnable) {
// make sure these are initialized
retVal = -1;
retThrowable = null;
// call into the EDT
SwingUtilities.invokeLater(runnable);
// wait for the runnable to finish
while (retVal == -1 && retThrowable == null) {
try {
runnable.wait();
} catch (InterruptedException ie) {
// short process, safe to ignore interrupts
}
}
// if the delegate threw a throwable, rethrow it here
if (retThrowable != null) {
if (retThrowable instanceof PrinterException) {
throw (PrinterException) retThrowable;
} else if (retThrowable instanceof Exception) {
throw (RuntimeException) retThrowable;
} else if (retThrowable instanceof Error) {
throw (Error) retThrowable;
}
// can not happen
throw new AssertionError(retThrowable);
}
return retVal;
}
}
}
}